Skip to content

Fix read_geotiff_gpu byteswap on big-endian multi-byte TIFFs#1515

Open
brendancol wants to merge 1 commit intoxarray-contrib:mainfrom
brendancol:fix-1508-gpu-byteswap
Open

Fix read_geotiff_gpu byteswap on big-endian multi-byte TIFFs#1515
brendancol wants to merge 1 commit intoxarray-contrib:mainfrom
brendancol:fix-1508-gpu-byteswap

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

Closes #1508.

cupy.ndarray (13.x) has no .byteswap() method, so any big-endian multi-byte TIFF was hitting AttributeError inside the GPU decode pipeline. The outer try/except in read_geotiff_gpu ate the error and quietly fell back to CPU, so users got correct results but lost the GPU fast path on BE data.

This swaps both arr.byteswap() calls in _gpu_decode.py for a small helper that views the array as the swapped-order dtype and copies. Works on both.

What changed

  • _gpu_decode.py: added _xp_byteswap(arr) helper, called at the two BE byteswap sites.
  • tests/test_gpu_byteswap_1508.py: new GPU regression test. Reads a BE TIFF (uint16, int16, uint32, int32, plus uncompressed uint16) via read_geotiff_gpu, asserts the result is a CuPy DataArray (not a NumPy fallback from a swallowed crash), and matches the CPU read.

Out of scope

The outer try/except Exception in read_geotiff_gpu that turns GPU errors into silent CPU fallback is itself worth revisiting; that's how this bug went undetected. Not touching it here. Filing as a follow-up.

GPU predictor=2 on BE data is also broken, but in a separate way: the predictor kernel decodes the byte stream as native LE before the byteswap. That's a kernel-level issue, not a byteswap one. Also a follow-up.

Test plan

  • pytest xrspatial/geotiff/tests/test_gpu_byteswap_1508.py -q -> 5 passed
  • pytest xrspatial/geotiff/tests/ -q -> 695 passed, 4 skipped (3 pre-existing matplotlib palette failures unrelated)

cupy.ndarray (13.x) does not expose .byteswap(), so any BE multi-byte
TIFF hit AttributeError inside the GPU decode pipeline. The dispatcher
in read_geotiff_gpu caught it and silently fell back to CPU, so output
stayed correct but the GPU path was effectively dead for BE data.

Replace both arr.byteswap() calls with a small helper that views the
array as the swapped-order dtype and copies, which works on numpy and
cupy arrays alike.

Closes xarray-contrib#1508
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes the read_geotiff_gpu fast-path for big-endian, multi-byte GeoTIFFs on CuPy (which lacks cupy.ndarray.byteswap()), so BE tiled TIFFs decode on GPU instead of silently falling back to CPU.

Changes:

  • Added _xp_byteswap(arr) helper in xrspatial/geotiff/_gpu_decode.py and replaced two out.byteswap() call sites.
  • Added GPU regression tests to ensure BE multi-byte TIFFs remain CuPy-backed and match CPU output.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
xrspatial/geotiff/_gpu_decode.py Introduces a cross-backend byteswap helper and uses it in the BE post-processing steps.
xrspatial/geotiff/tests/test_gpu_byteswap_1508.py Adds a regression test to confirm GPU decoding is exercised for BE multi-byte TIFFs and matches CPU results.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +63 to +69
(as of cupy 13.x) does not. The view-then-copy trick works on both:
re-interpret the buffer as the swapped-order dtype, then copy to
materialise the swapped bytes as a real array in that dtype.
"""
return arr.view(arr.dtype.newbyteorder()).copy()


Comment on lines +51 to +55
assert isinstance(gpu_da.data, cupy.ndarray), (
"expected cupy-backed DataArray, got "
f"{type(gpu_da.data).__name__} -- the GPU path likely fell back "
"to CPU again"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GPU read of big-endian multi-byte TIFFs crashes on cupy.ndarray.byteswap()

2 participants